package tool

import akka.stream.Materializer

import java.io.{File, FileOutputStream, FilenameFilter}
import dao.{HmdbKeggInfoDao, HmdbOtherNameKeggDao, KeggInfoDao, MatrixFileDataDao, MissionDao, WorkflowMissionDao}
import implicits.Implicits._
import play.api.mvc.{AnyContent, MultipartFormData, Request, RequestHeader}
import tool.Pojo._
import utils.Utils

import scala.concurrent.ExecutionContext.Implicits.global
import implicits.Implicits._
import models.Tables.WorkflowMissionRow
import org.apache.commons.io.FileUtils
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import play.api.i18n.Messages
import play.api.libs.Files.TemporaryFile
import play.api.libs.ws.WSClient
import scalax.collection.GraphEdge.DiEdge
import scalax.collection.mutable.Graph
import shared.Pojo._
import shared.pojo.WorkflowMission.FsData
import shared.{SharedTool, VarTool}
import tool.plot.AllPlot
import tool.plot.AllPlot.RPlotB
import tool.plot._
import tool.soft.AllSoft
import tool.soft.AllSoft._
import tool.workflow.AllWorkflow
import tool.workflow.fsSoft.{AllFsSoft, FSDiagnoseAna, Intersect, Union}

import scala.collection.immutable.SeqMap
import scala.concurrent.Future

/** Created by yz on 13/7/2020
  */
trait ProjectTool {

  val softs = AllSoft.softs
  val workflows = AllWorkflow.workflows

  val kindRPlotMap = AllPlot.rPlots.map { x =>
    (x.name, x)
  }.toMap

  val kindMyRPlotMap = AllPlot.myRPlots.map { x =>
    (x.name, x)
  }.toMap

  val kindSoftMap = softs.map { x =>
    (x.nameStr -> x)
  }.toMap

  val kindWorkflowMap = workflows.map { x =>
    (x.nameStr -> x)
  }.toMap

  def getScaleMethodMap(implicit messages: Messages) = SeqMap(
    "center" -> messages("centering"),
    "scale" -> messages("zScaling"),
    "pareto" -> messages("paretoScaling"),
    "none" -> messages("none")
  )

  def long2fat(workspaceDir: File, resultDir: File) = {
    val command =
      s"""
         |Rscript ${Tool.rPath.unixPath}/long2fat.R
                       """.stripMargin
    CommandData(workspaceDir, List(command))
  }

  def preProcess(workspaceDir: File, resultDir: File) = {
    val command =
      s"""
         |Rscript ${Tool.rPath.unixPath}/naIdentify.R
                       """.stripMargin
    CommandData(workspaceDir, List(command))
  }

  def hJoin(workspaceDir: File, resultDir: File) = {
    val command =
      s"""
         |Rscript ${Tool.rPath.unixPath}/hJoin.R
                       """.stripMargin
    CommandData(workspaceDir, List(command))
  }

  def isMulGroup(workspaceDir: File) = {
    val groupFile = new File(workspaceDir, "group.txt")
    groupFile.txtLines.lineSeqMapLower
      .map { map =>
        map("classnote")
      }
      .distinct
      .size > 2
  }

  def isSingleGroup(groupFile: File) = {
    groupFile.txtLines.lineSeqMapLower.map(_("classnote")).toSet.size == 1
  }

  def long2fatResultDeal(workspaceDir: File, resultDir: File) = {
    val resultFile = new File(workspaceDir, "output.csv")
    resultFile.copyTo(new File(resultDir, "result.csv"))
  }

  def preprocessResultDeal(workspaceDir: File, resultDir: File) = {
    workspaceDir
      .listFiles()
      .filter { x =>
        (!x.getName.endsWith(".txt") && x.getName.matches("^\\d+.*$")) || x.getName.endsWith(".pdf")
      }
      .foreach { file =>
        file.fileCopyToDir(resultDir)
      }

  }

  sealed trait ConvertType
  case object Pdf2Png extends ConvertType

  def fileConvertCommandIfExist(dir: File, fileNamePrefix: String, convertType: ConvertType = Pdf2Png) = {
    if (new File(dir, s"${fileNamePrefix}.pdf").exists()) {
      s"convert  -density 300 ${fileNamePrefix}.pdf ${fileNamePrefix}.png"
    } else ""
  }

  def fileConvertCommandsIfExist(dir: File, fileNamePrefixs: List[String], convertType: ConvertType = Pdf2Png) = {
    fileNamePrefixs.map { fileNamePrefix =>
      fileConvertCommandIfExist(dir, fileNamePrefix, convertType)
    }
  }

  def getTrueOutDir(idStr: String)(implicit messages: Messages) = {
    val kind = SharedTool.getKind(idStr)
    val outKind = messages(AllFsSoft.kindSoftMap(kind).messageName).replaceAll("/", "&&")
    val id = SharedTool.getId(idStr)
    val idSuffix = if (id == 0) "" else s"_${id}"
    s"${outKind}${idSuffix}"
  }

  def featureSelectionResultDeal(workspaceDir: File, resultDir: File, graph: Graph[String, DiEdge])(implicit
      ws: WSClient,
      materializer: Materializer,
      messages: Messages
  ) = {
    workspaceDir.listFiles().foreach { x =>
      if (x.isDirectory) {
        val dirName = x.getName
        if (dirName != "dataFileNode") {
          val trueOutDir = Tool.getTrueOutDir(dirName)
          val kind = SharedTool.getKind(dirName)
          val index = SharedTool.getId(dirName)
          x.listFiles(fileNameFilter).foreach { subFile =>
            val subDestDir = new File(resultDir, s"${trueOutDir}").createDirectoryWhenNoExist
            subFile.fileCopyToDir(subDestDir)
          }
          val soft = AllFsSoft.kindSoftMap(kind)
          val sourceDir = new File(workspaceDir, dirName)
          soft.resultCopy(sourceDir, resultDir, index)
        }
      } else if (!x.getName.endsWith(".txt") && !(x.getName == "config.json")) {
        x.fileCopyToDir(resultDir)
      }
    }
    workspaceDir
      .listFiles()
      .map { x =>
        if (x.isDirectory) {
          val dirName = x.getName
          if (dirName != "dataFileNode") {
            val trueOutDir = Tool.getTrueOutDir(dirName)
            val dataFileNode = graph.find("dataFileNode").get
            val curNode = graph.find(dirName).get
            val path = (dataFileNode shortestPathTo curNode).get
            val finalOutDir = path.nodes.init
              .filter { x =>
                List(Union, Intersect).map(_.nameStr).contains(SharedTool.getKind(x))
              }
              .map { x =>
                Tool.getTrueOutDir(x)
              }
              .mkString("/")
            if (finalOutDir != "") {
              val destDir = new File(resultDir, s"${finalOutDir}/${trueOutDir}").deleteIfExist
              new File(resultDir, trueOutDir).dirMoveToIfExist(destDir)
            }
            (trueOutDir, finalOutDir)
          } else ("", "")
        } else ("", "")
      }
      .toList
      .toSeqMap
  }

  def isValidFileName(name: String, excludeSuffixs: List[String] = List.empty) = {
    val suffixs = List(".pdf", ".csv", ".svg", ".html", ".js", ".xlsx", "xls", "gml").filterNot { x =>
      excludeSuffixs.contains(x)
    }
    suffixs.exists(x => name.endsWith(x)) && (!name.startsWith("Rplots"))
  }

  def getFileNameFilter(excludeSuffixs: List[String] = List.empty) = {
    new FilenameFilter {
      override def accept(dir: File, name: String): Boolean = {
        isValidFileName(name, excludeSuffixs)
      }
    }
  }

  val fileNameFilter = new FilenameFilter {
    override def accept(dir: File, name: String): Boolean = {
      isValidFileName(name)
    }
  }

  def resultDeal(workspaceDir: File, resultDir: File): Unit = {
    workspaceDir.listFiles(fileNameFilter).foreach { file =>
      file.fileCopyToDir(resultDir)
    }
  }

  def resultDeal(workspaceDir: File, resultDir: File, excludeFileNames: List[String]): Unit = {
    workspaceDir.listFiles(fileNameFilter).filterNot { file => excludeFileNames.contains(file.getName) }.foreach {
      file =>
        file.fileCopyToDir(resultDir)
    }
  }

  def selectHmdbKeggInfos(names: Seq[String])(implicit dao: MyDao) = {
    val hmdbKeggInfoDao = dao.hmdbKeggInfoDao
    val hmdbOtherNameKeggDao = dao.hmdbOtherNameKeggDao
    val f = hmdbKeggInfoDao.selectAll(names).zip(hmdbOtherNameKeggDao.selectAll(names)).flatMap {
      case (keggInfos, otherNameKeggs) =>
        val keggInfoMap = keggInfos.map { row =>
          (row.cleanName, row)
        }.toMap
        val otherMap = otherNameKeggs.map { row =>
          (row.cleanName, row)
        }.toMap
        val idMap = names
          .map { name =>
            if (keggInfoMap.isDefinedAt(name)) {
              (name, name)
            } else if (otherMap.isDefinedAt(name)) {
              (Tool.cleanName(otherMap(name).name), name)
            } else ("", "")
          }
          .filter(_._1.nonEmpty)
          .toMap
        hmdbKeggInfoDao.selectAll(idMap.keys.toList).map { keggInfos =>
          keggInfos.map { row =>
            val cleanName = idMap(row.cleanName)
            KeggInfoData("", row.accession, row.keggId, row.superClass, cleanName)
          }
        }
    }
    Utils.execFuture(f)
  }

  def selectKeggInfos(names: Seq[String])(implicit dao: MyDao) = {
    case class MyKegginfoRow(
        index: String,
        name: String,
        strandardName: String,
        accession: String,
        keggid: String,
        superclass: String,
        cleanName: String,
        strandardCleanName: String
    )
    val f = dao.keggInfoDao.selectAll.map { tmpKeggInfos =>
      val keggInfos = tmpKeggInfos.map { x =>
        MyKegginfoRow(
          index = x.index,
          name = x.name,
          strandardName = x.strandardName,
          accession = x.accession,
          keggid = x.keggId,
          superclass = x.superClass,
          cleanName = Tool.cleanName(x.name),
          strandardCleanName = Tool.cleanName(x.strandardName)
        )
      }.toList
      val nameKeggInfos = keggInfos.distinctByKeepHead(_.cleanName)
      val nameKeggInfoMap = nameKeggInfos.map { row =>
        (row.cleanName, row)
      }.toMap
      val standradNameKeggInfos = keggInfos.distinctByKeepHead(_.strandardCleanName)
      val standradNameKeggInfoMap = standradNameKeggInfos.map { row =>
        (row.strandardCleanName, row)
      }.toMap
      val rows = names
        .map { name =>
          if (nameKeggInfoMap.isDefinedAt(name)) {
            val row = nameKeggInfoMap(name)
            KeggInfoData(row.strandardName, row.accession, row.keggid, row.superclass, row.cleanName)
          } else if (standradNameKeggInfoMap.isDefinedAt(name)) {
            val row = standradNameKeggInfoMap(name)
            KeggInfoData("", row.accession, row.keggid, row.superclass, row.strandardCleanName)
          } else KeggInfoData("", "", "", "", "")
        }
        .filter(_.cleanName.nonEmpty)
      rows
    }
    Utils.execFuture(f)
  }

  def cleanName(name: String) = {
    val newName = Tool.cleanSpecialChr(name.trim)
    val symbols = List("_")

    def loop(symbols: List[String], name: String): String = {
      symbols match {
        case x :: xs =>
          val curName = name.replace(x, " ")
          loop(xs, curName)
        case Nil => name
      }
    }

    val replaceNewName = loop(symbols, newName)
    val finalName = replaceNewName.replace("?", "").replaceAll("\\s+", " ")
    finalName.toLowerCase
  }

  def cleanSpecialChr(name: String) = {

    def loop(letterT: List[(String, String)], name: String): String = {
      letterT match {
        case ::(head, next) =>
          val (letter, text) = head
          val curName = name.replace(letter, text)
          loop(next, curName)
        case Nil => name
      }

    }

    val letterT = letterMap.toList
    loop(letterT, name)
  }

  lazy val letterMap = {
    val letterFile = new File(Tool.dataDir, "replace_info.txt")
    letterFile.txtLines.map { columns =>
      (columns(0) -> columns(1))
    }.toMap
  }

  def produceKeggInfoFile(dataFile: File, workspaceDir: File)(implicit dao: MyDao) = {
    val map = dataFile.txtLines
      .drop(1)
      .map { columns =>
        val name = columns(0)
        val cleanName = Tool.cleanName(name)
        (cleanName, name)
      }
      .toMap
    val names = map.keys.toList
    val userKeggInfos = Tool.selectKeggInfos(names).toList
    val hmdbKeggInfos = Tool.selectHmdbKeggInfos(names).toList

    val keggInfos = (userKeggInfos ::: hmdbKeggInfos).distinctByKeepHead(_.cleanName)

    def ts2File = {
      implicit val sep = "\t"
      val headers = List("Class", "Raw_Metabolite", "Metabolite", "HMDB", "KEGG")
      val newLines = keggInfos.map { case keggInfo =>
        val accession = keggInfo.accession
        val name = keggInfo.cleanName
        val orignalName = map.getOrElse(name, "")
        val finalName = keggInfo.finalName
        val trueFinalName = if (finalName.isBlank) orignalName else finalName
        List(keggInfo.superclass, orignalName, trueFinalName, keggInfo.accession, keggInfo.keggid)
      }.distinct

      val tmpLines = newLines.distinctByKeepHead(_(1))
      val finalLines = tmpLines
        .groupBy(_(2))
        .mapValues { lines =>
          lines.zipWithIndex.map { case (columns, i) =>
            if (i == 0) columns else columns.updated(2, s"${columns(2)}_${i}")
          }
        }
        .values
        .flatten
        .toList

      (headers :: finalLines).toTxtFile(new File(workspaceDir, "keggInfo.txt"))
    }

    ts2File

  }

  def producePairsInfoFile(dataFile: File, workspaceDir: File, species: String) = {
    val parent = new File(Tool.dataDir, "species")
    val file = new File(parent, s"${species}.txt")
    val set = file.lines.drop(1).flatMapSetByColumns { columns =>
      val subs = columns(3).split(" + ")
      val pros = columns(4).split(" + ")
      subs.flatMap { sub =>
        pros.map { pro =>
          s"${pro}/${sub}"
        }
      }.toSet
    }
    val keggInfoFile = new File(workspaceDir, "keggInfo.txt")
    val headers = keggInfoFile.txtLines.head
    val keggIds = keggInfoFile.lines.filterLineSize(_ >= 5).mapOtherByColumns { columns =>
      val map = headers.zip(columns).toMap
      map("KEGG")
    }
    val keggSets = keggIds
      .combinations(2)
      .flatMap { keggIds =>
        Set(s"${keggIds.head}/${keggIds.last}", s"${keggIds.last}/${keggIds.head}")
      }
      .toSet
    val finalSet = keggSets.intersect(set)
    finalSet.toList.toFile(new File(workspaceDir, "pairInfo.txt"))

  }

  def getInitFile(data: AddData, tmpDir: File, defaultFileName: String)(implicit
      request: Request[MultipartFormData[TemporaryFile]],
      messages: Messages
  ) = {
    data.inputKind match {
      case x if x == messages("UsingTheSampleFile") =>
        val inputFile = new File(tmpDir, defaultFileName)
        new File(Tool.exampleDir, defaultFileName).copyTo(inputFile)
      case x if x == messages("file") =>
        val tmpFile = Tool.fileMoveDir("file", tmpDir)
        val inputFile = if (tmpFile.nameSuffix.equalsIgnoreCase("biom")) {
          tmpFile
        } else new File(tmpDir, s"${tmpFile.namePrefix.addSuffix}")
        tmpFile.nameSuffix match {
          case "csv" =>
            tmpFile.csvLines.toTxtFile(inputFile)
          case "xlsx" =>
            tmpFile.xlsxLines().toTxtFile(inputFile)
          case _ =>
        }
        inputFile
    }
  }

  def headerMapDeal(headerMap: SeqMap[String, String]) = {
    headerMap.flatMap { case (k, v) =>
      if (!k.equalsIgnoreCase(v) && (!headerMap.containsIgnoreCase(v))) {
        SeqMap(k -> v, v -> s"before_${v}")
      } else SeqMap(k -> v)
    }
  }

  def getNewFileName(fileNames: List[String], file: File): String = {
    val fileName = file.getName
    getNewFileName(fileNames, fileName)
  }

  def getNewFileName(fileNames: List[String], fileName: String): String = {
    if (!fileNames.contains(fileName)) {
      fileName
    } else {
      val fileNamePrefix = fileName.fileNamePrefix
      val fileNameSuffix = fileName.fileNameSuffix
      val ids = fileNames
        .filter(_.fileNameSuffix == fileNameSuffix)
        .map(_.fileNamePrefix)
        .filter(x => x.matches(s"^${fileNamePrefix}_(\\d+)$$"))
        .map(_.replaceAll("^" + s"${fileNamePrefix}_", "").toInt)
      val idMax = if (ids.isEmpty) 1 else ids.max
      val diffIds = (1 to idMax).diff(ids)
      val appendId = if (diffIds.nonEmpty) {
        diffIds.head
      } else ids.max + 1
      s"${fileNamePrefix}_${appendId}.${fileNameSuffix}"
    }
  }

  def produceRawDataLinesF(userId: Int, fileName: String)(implicit missionDao: MissionDao) = {
    val names = fileName.split("/")
    val isMission = names(0) == "mission"
    val fileF = Tool.produceRawDataFileF(userId, fileName)
    val f = fileF.map { dataFile =>
      if (isMission) dataFile.csvLines else dataFile.txtLines
    }
    f
  }

  def getGroupNum(userId: Int, fileName: String): Int = {
    val names = fileName.mySplit("/")
    if (fileName.isBlank) {
      1
    } else {
      val rawGroupFile = Tool.getGroupFile(userId, names)
      Tool.getGroupNum(rawGroupFile)
    }

  }

  def getIsPairedStr(isPaired: Boolean)(implicit messages: Messages) = {
    if (isPaired) messages("yes") else messages("no")
  }

  def getGroupNum(groupFile: File): Int = {
    val lines = groupFile.txtLines
    val maps = lines.lineSeqMapLower
    maps.map(_("classnote")).toSet.size
  }

  def getMethod(method: String)(implicit messages: Messages) = {
    method match {
      case "u" => messages("uTest")
      case "t" => messages("tTest")
      case "auto" => messages("wiseChoose")
    }
  }

  def getFcMethod(fcMethod: String)(implicit messages: Messages) = {
    fcMethod match {
      case "median" => "Median"
      case "mean" => "Mean"
      case "auto" => messages("wiseChoose")
    }
  }

  def getMulMethod(method: String)(implicit messages: Messages) = {
    method match {
      case "kw" => messages("kruskalWallisTest")
      case "anova" => messages("anovaTest")
      case "auto" => messages("wiseChoose")
    }
  }

  def getMyFileData(file: File) = {
    MyFileData(file.getName, "other", "", file.size)
  }

  def produceRawDataFileF(userId: Int, fileName: String)(implicit missionDao: MissionDao) = {
    val names = fileName.mySplit("/")
    val isMission = names(0) == "mission"
    val fileF = if (isMission) {
      val missionName = names(1)
      missionDao.selectOp(userId, missionName).map { rowOp =>
        rowOp
          .map { row =>
            val resultDir = Tool.getMissionResultDir(row)
            new File(resultDir, names(2))
          }
          .getOrElse {
            new File("must_no_exist.txt").deleteIfExist
          }
      }
    } else if (names(0) == "example") {
      Future {
        val kindDir = Tool.softExampleDir
        new File(kindDir, names.tail.mkString("/"))
      }
    } else {
      Future {
        val kindDir = Tool.getUserDataKindDir(userId, VarTool.showDirNameMap(names(0)).name)
        new File(kindDir, names(1))
      }
    }
    fileF
  }

  def getFilesExistF(userId: Int, fileNames: List[String])(implicit missionDao: MissionDao) = {
    val fileNameNames = fileNames.map { fileName =>
      fileName.mySplit("/")
    }
    val missionFileNames = fileNameNames.filter { names =>
      names(0) == "mission"
    }
    val exampleFileNames = fileNameNames.filter { names =>
      names(0) == "example"
    }
    val kindFileNames = fileNameNames.filterNot { names =>
      missionFileNames.contains(names) || exampleFileNames.contains(names)
    }
    val kindFileNameInfo = kindFileNames.map { names =>
      val kindDir = Tool.getUserDataKindDir(userId, VarTool.showDirNameMap(names(0)).name)
      (names, new File(kindDir, names(1)).exists())
    }
    val exampleFileNameInfo = exampleFileNames.map { names =>
      val kindDir = new File(Tool.softExampleDir, names(1))
      (names, new File(kindDir, names(2)).exists())
    }
    val missionNameNamesMap = missionFileNames
      .map { names =>
        (names(1), names(2))
      }
      .groupMap(_._1)(_._2)
    val missionNames = missionFileNames.map(_(1))
    missionDao.selectAll(userId, missionNames).map { rows =>
      val fileNameExistTs = rows.toList.flatMap { row =>
        val resultDir = Tool.getMissionResultDir(row)
        val names = missionNameNamesMap(row.missionName)
        names.map { name =>
          val file = new File(resultDir, name)
          (List("mission", row.missionName, name), file.exists())
        }
      }
      (fileNameExistTs ::: kindFileNameInfo ::: exampleFileNameInfo).map { case (names, exist) =>
        (names.mkString("/"), exist)
      }
    }
  }

  def produceRawDataFile(userId: Int, fileName: String, rawDataFile: File)(implicit dao: MyDao): File = {
    implicit val missionDao = dao.missionDao
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines.toTxtFile(rawDataFile)
    }
    Utils.execFuture(fileF)
  }

  def getFileNameOrId(userId: Int, fileName: String)(implicit matrixFileDataDao: MatrixFileDataDao) = {
    val names = fileName.mySplit("/")
    if (names(0) == MetDataKind.showDirName) {
      val f = matrixFileDataDao.select(userId, names(1)).map(_.id)
      val id = Utils.execFuture(f)
      s"${names(0)}/${id}"
    } else {
      fileName
    }
  }

  def getTrueFileName(fileName: String)(implicit matrixFileDataDao: MatrixFileDataDao) = {
    val names = fileName.mySplit("/")
    if (names(0) == MetDataKind.showDirName && names(1).isInt) {
      val f = matrixFileDataDao.select(names(1).toInt).map(_.fileName)
      val subFileName = Utils.execFuture(f)
      s"${names(0)}/${subFileName}"
    } else {
      fileName
    }
  }

  def getMatrixFileData(fileName: String)(implicit matrixFileDataDao: MatrixFileDataDao) = {
    val names = fileName.mySplit("/")
    val f = matrixFileDataDao.select(names(1).toInt)
    Utils.execFuture(f)
  }

  def produceRawGroupFile(userId: Int, fileName: String, groupFile: File)(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines.convertHeader(Map("classnote" -> "ClassNote", "sample" -> "Sample")).toTxtFile(groupFile)
    }
    Utils.execFuture(fileF)
  }

  def produceRawGroupFile(userId: Int, fileName: String, groupFile: File, headerMap: SeqMap[String, String])(implicit
      dao: MyDao
  ) = {
    implicit val missionDao = dao.missionDao
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines.convertHeader(headerMap).toTxtFile(groupFile)
    }
    Utils.execFuture(fileF)
  }

  def produceRawGroupFile(userId: Int, fileName: String, dataFile: File, groupFile: File)(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    Tool.produceRawGroupLines(userId, fileName, dataFile).toTxtFile(groupFile)
  }

  def produceRawGroupLines(userId: Int, fileName: String, dataFile: File)(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    val sampleIds = dataFile.txtLines.head.drop(1)
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines
        .convertHeader(Map("classnote" -> "ClassNote", "sample" -> "Sample"))
        .lineSeqMap
        .filter { map =>
          sampleIds.contains(map("Sample"))
        }
        .csvLines
    }
    Utils.execFuture(fileF)
  }

  def produceRawVarInfoFile(userId: Int, fileName: String, dataFile: File, varInfoFile: File)(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    val metabolites = dataFile.txtLines.map(_.head).drop(1)
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines
        .convertHeader(Map("metabolite" -> "Metabolite"))
        .lineSeqMap
        .filter { map =>
          metabolites.contains(map("Metabolite"))
        }
        .csvLines
        .toTxtFile(varInfoFile)
    }
    Utils.execFuture(fileF)
  }

  def produceRawVarInfoFile(
      userId: Int,
      fileName: String,
      dataFile: File,
      varInfoFile: File,
      selectColumns: List[String]
  )(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    val metabolites = dataFile.txtLines.map(_.head).drop(1)
    val trueSelectColumns = "Metabolite" :: selectColumns
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines
        .convertHeader(Map(0 -> "Metabolite"))
        .selectColumns(trueSelectColumns)
        .lineSeqMap
        .filter { map =>
          metabolites.contains(map("Metabolite"))
        }
        .csvLines
        .toTxtFile(varInfoFile)
    }
    Utils.execFuture(fileF)
  }

  def produceRawMetaboliteFile(userId: Int, fileName: String, dataFile: File)(implicit dao: MyDao) = {
    implicit val missionDao = dao.missionDao
    val fileF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
      lines.convertHeader(Map(0 -> "Metabolite")).toTxtFile(dataFile)
    }
    Utils.execFuture(fileF)
  }

  def produceNoGroupFile(rawDataFile: File, groupFile: File)(implicit dao: MyDao) = {
    val header = List("Sample", "ClassNote")
    val lines = rawDataFile.txtLines.head.drop(1).map { sampleId =>
      List(sampleId, "noGroup")
    }
    (header :: lines).toTxtFile(groupFile)
  }

  def produceSampleColorFile(workspaceDir: File, colors: List[String]): File = {
    val sampleColorFile = Tool.getSampleColorFile(workspaceDir)
    Tool.produceSampleColorFile(workspaceDir, colors, sampleColorFile)
  }

  def produceSampleColorFile(workspaceDir: File, colors: List[String], sampleColorFile: File): File = {
    val lines = sampleColorFile.txtLines
    val header = lines.head
    val newLines = lines.drop(1).zip(colors).map { case (columns, color) =>
      List(columns.head, color)
    }
    (header :: newLines).toTxtFile(sampleColorFile)
  }

  def getTFStr(tf: String)(implicit messages: Messages) = {
    if (tf == "T") messages("yes") else messages("no")
  }

  def produceSampleColorFile(workspaceDir: File, colors: List[String], name: String): File = {
    val sampleColorFile = Tool.getSampleColorFile(workspaceDir, name)
    Tool.produceSampleColorFile(workspaceDir, colors, sampleColorFile)
  }

  def setDefaultPlotData(tmpPlotData: AllPlotData, rPlot: RPlotB) = {
    rPlot.defaultPlotArg(tmpPlotData)
  }

  def dataCoverConfigFile(data: AllPlotData, workspaceDir: File, configFileName: String, configJsonFileName: String) = {
    data.lines().toTxtFile(new File(workspaceDir, configFileName))
    Utils.t2ConfigFile(data, new File(workspaceDir, s"${configJsonFileName}"))
  }

  def producePlotConfigFile(tmpPlotData: AllPlotData, rPlot: RPlotB, workspaceDir: File) = {
    val data = rPlot.defaultPlotArg(tmpPlotData)
    data.lines().toTxtFile(Tool.getPlotConfigTxtFile(workspaceDir, rPlot.name))
    Utils.t2ConfigFileWithDefault(data, new File(workspaceDir, rPlot.configJsonFileName))
  }

  def producePlotConfigFile(tmpPlotData: AllPlotData, rPlot: RPlotB, workspaceDir: File, prefix: String) = {
    val data = rPlot.defaultPlotArg(tmpPlotData)
    data.lines().toTxtFile(new File(workspaceDir, s"${prefix}_${rPlot.configFileName}"))
    Utils.t2ConfigFile(data, new File(workspaceDir, s"${prefix}_${rPlot.configJsonFileName}"))
  }

  def getFileData(row: WorkflowMissionRow) = {
    MissionIdFileData(row.userId, row.kind, row.id)
  }

  def varUniqDeal(tmpFile: File) = {
    val lines = tmpFile.txtLines
    val indexMap = lines
      .drop(1)
      .zipWithIndex
      .groupMap { x =>
        x._1.head
      }(_._2)
      .values
      .flatMap { indexs =>
        indexs.zipWithIndex
      }
      .toMap
    val newLines = lines.head :: lines.drop(1).zipWithIndex.map { case (columns, i) =>
      val index = indexMap(i)
      val suffix = if (index == 0) "" else s"_${index}"
      s"${columns.head}${suffix}" :: columns.tail
    }
    newLines.toTxtFile(tmpFile)
  }

  def na2zero(lines: List[List[String]]) = {
    lines.head :: lines.drop(1).map { columns =>
      columns.head :: columns.drop(1).map { v =>
        if (!v.isDouble) {
          "0"
        } else v
      }
    }
  }

  def getWorkspaceDirF(idStrOp: Option[String], missionId: Int)(implicit
      workflowMissionDao: WorkflowMissionDao,
      missionDao: MissionDao
  ) = {
    getWorkspaceResultDirF(idStrOp, missionId).map { case (workspaceDir, resultDir) =>
      workspaceDir
    }
  }

  def getWorkspaceResultDirF(idStrOp: Option[String], missionId: Int)(implicit
      workflowMissionDao: WorkflowMissionDao,
      missionDao: MissionDao
  ) = {
    idStrOp
      .map { kind =>
        workflowMissionDao.select(missionId).map { row =>
          val fileData = Tool.getFileData(row)
          val workspaceDir = WorkflowMissionFileTool.getMissionWorkspaceDir(fileData)
          val resultDir = WorkflowMissionFileTool.getMissionResultDir(fileData)
          (workspaceDir, resultDir)
        }
      }
      .getOrElse {
        missionDao.select(missionId).map { row =>
          val workspaceDir = Tool.getMissionWorkspaceDir(row)
          val resultDir = Tool.getMissionResultDir(row)
          (workspaceDir, resultDir)
        }
      }
  }

  def produceSubConfigFile(workspaceDir: File, index: Int, convertMap: SeqMap[String, String]) = {
    val configFile = new File(workspaceDir.getParentFile, "calculate_config.txt")
    val subConfigFile = new File(workspaceDir, "calculate_config.txt")
    configFile.txtLines
      .map { tmpColumns =>
        val columns = tmpColumns.padTo(2, "")
        val tails = columns.last.mySplit(";")
        val head = convertMap.getOrElse(columns.head, columns.head)
        val finalTail = if (tails.size > index) {
          tails(index)
        } else tails.mkString(";")
        List(head, finalTail)
      }
      .toTxtFile(subConfigFile)
  }

  def inputMetabolites(dir: File) = {
    val file = Tool.getAllMetRawFile(dir)
    file.txtLines.lineSeqMap.map(_("Metabolite"))
  }

}
